package com.yuandian.management.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yuandian.api.management.entity.*;
import com.yuandian.api.management.enums.GraphType;
import com.yuandian.api.management.vo.ConfNetTopologyVo;
import com.yuandian.bpm.common.core.util.R;
import com.yuandian.bpm.common.security.util.SecurityUtils;
import com.yuandian.management.mapper.NetTopologyComponentMapper;
import com.yuandian.management.mapper.NetTopologyConfigMapper;
import com.yuandian.management.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 说明：通用接口配置服务实现
 * @Author zfsn
 * @Date 2023/2/20 19:59
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class NetTopologyConfigServiceImpl implements NetTopologyConfigService {


	private final ConfSegmentService confSegmentService;


	private final NetTopologyConfigMapper netTopologyConfigMapper;
	private final ConfComponentInfoConfigService confComponentInfoConfigService;

	private final NetTopologyComponentMapper netTopologyComponentMapper;

	private final ConfGraphService confGraphService;

	private final ConfNodePropertyService confNodePropertyService;
	private final ConfComponentIpService confComponentIpService;

	/**
	 * 通过系统id 获取it路径图
	 *
	 * @param systemId 系统id
	 * @return 通用返回对象
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public List<Map<String, Object>> getRoadMap(Integer systemId) {

		// 1. 获取节点信息
		QueryWrapper<ConfNetTopology> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("system_id", systemId);
		Map<Integer, String> confSegmentsMap = netTopologyConfigMapper.selectList(queryWrapper).stream().collect(Collectors.toMap(ConfNetTopology::getId, ConfNetTopology::getComponentName));

		// 2. 获取节点关系信息
		QueryWrapper<ConfGraph> queryWrapperGraph = new QueryWrapper<>();
		queryWrapperGraph.eq("graph_id", systemId);
		queryWrapperGraph.eq("type", GraphType.NET_TOPOLOGY_RELATIONSHIP.getType());
		List<ConfGraph> confGraphs = confGraphService.list(queryWrapperGraph);

		// 2. 获取节点ip和端口
		QueryWrapper<ConfNetTopologyComponentIp> queryWrapperComponentIp = new QueryWrapper<>();
		List<ConfNetTopologyComponentIp> confComponentIps = netTopologyComponentMapper.selectList(queryWrapperComponentIp);


		// 4. 获取节点坐标等信息
		LambdaQueryWrapper<ConfNodeProperty> propertyLambdaQueryWrapper = Wrappers.lambdaQuery();
		propertyLambdaQueryWrapper.eq(ConfNodeProperty::getType, GraphType.NET_TOPOLOGY_RELATIONSHIP.getType());
		propertyLambdaQueryWrapper.eq(ConfNodeProperty::getGraphId, systemId);
		Map<String, ConfNodeProperty> nodePropertyIdAndObjectMap = confNodePropertyService.list(propertyLambdaQueryWrapper).stream().collect(Collectors.toMap(ConfNodeProperty::getNodeId, v -> v, (k1, k2) -> k2));

		if (nodePropertyIdAndObjectMap.size() == 0) {
			return new ArrayList<>();
		}
		// 将上面信息组装成 多节点的树结构, 返回给前端
		return generateRoadMap(confComponentIps, confGraphs, confSegmentsMap, nodePropertyIdAndObjectMap, systemId);
	}

	/**
	 * 增加节点
	 *
	 * @param confSegment
	 * @return 通用返回对象
	 */
	@Override
	public R<String> addNode(ConfNetTopologyVo confSegment) {

		// 1. 保存网端信息
		ConfComponentInfoConfig confComponentInfoConfig = new ConfComponentInfoConfig();
		confComponentInfoConfig.setSystemId(confSegment.getSystemId());
		confComponentInfoConfig.setComponentName(confSegment.getComponentName());
		confComponentInfoConfig.setConnectType(confSegment.getConnectType());
		confComponentInfoConfig.setDeviceType(confSegment.getDeviceType());
		confComponentInfoConfig.setNetType(confSegment.getNetType());
		confComponentInfoConfig.setCreateBy(SecurityUtils.getUser().getName());
		confComponentInfoConfigService.save(confComponentInfoConfig);

		// 2. 保存节点位置坐标等信息
		ConfComponentInfoConfig configServiceOne = confComponentInfoConfigService.getOne(Wrappers.query(confComponentInfoConfig));

		ConfNodeProperty confNodeProperty = new ConfNodeProperty();
		confNodeProperty.setType(GraphType.NET_TOPOLOGY_RELATIONSHIP.getType());
		confNodeProperty.setGraphId(String.valueOf(confSegment.getSystemId()));
		confNodeProperty.setNodeId(String.valueOf(configServiceOne.getId()));
		confNodeProperty.setLocationX(confSegment.getLocationX());
		confNodeProperty.setLocationY(confSegment.getLocationY());
		confNodeProperty.setCreateBy(SecurityUtils.getUser().getName());
		confNodeProperty.setCreateTime(LocalDateTime.now());
		boolean result = confNodePropertyService.save(confNodeProperty);
		return R.ok(result ? "增加节点成功": "增加节点失败");
	}

	/**
	 * 增加节点连线
	 *
	 * @param confGraph
	 * @return 通用返回对象
	 */
	@Override
	public R<String> addLink(ConfGraph confGraph) {
		if(hasCircle(confGraph)){
			return R.failed("存在环,该资源不能够进行添加");
		}
		confGraph.setType(GraphType.NET_TOPOLOGY_RELATIONSHIP.getType());
		confGraph.setGraphId(String.valueOf(confGraph.getSystemId()));
		confGraph.setCreateBy(SecurityUtils.getUser().getName());
		confGraph.setCreateTime(LocalDateTime.now());
		return R.ok(confGraphService.save(confGraph) ? "增加节点连线成功": "增加节点连线失败");
	}

	/**
	 * 判断节点之间是否存在环
	 * @param confGraph
	 * @return
	 */
	private boolean hasCircle(ConfGraph confGraph) {
		LambdaQueryWrapper<ConfGraph> queryWrapper = Wrappers.lambdaQuery();
		queryWrapper.eq(ConfGraph::getType, GraphType.NET_TOPOLOGY_RELATIONSHIP.getType());
		queryWrapper.eq(ConfGraph::getGraphId, String.valueOf(confGraph.getSystemId()));
		List<ConfGraph> confGraphs = confGraphService.list(queryWrapper);

		// 如果节点之间没有连接, 则认为节点之间没有形成环状
		if (confGraphs.size() == 0) {
			return false;
		}
		confGraphs.add(confGraph);

		// 1. 统计每个节点的入度，并将入度为0的节点加入队列中
		Map<String, Integer> hashMap = new HashMap<>();
		Map<String, List<String>> map = new HashMap<>();
		for (ConfGraph node : confGraphs) {
			String id = node.getNodeId();
			String preId = node.getParentNodeId();
			map.computeIfAbsent(preId,k -> new ArrayList<>()).add(id);
			hashMap.put(id, hashMap.getOrDefault(id, 0) + 1);
		}
		Queue<String> queue = new LinkedList<>();
		for (String key : hashMap.keySet()) {
			if (hashMap.getOrDefault(key, 0) == 0){
				queue.offer(key);
			}
		}

		// 2. 对队列中的节点进行遍历，将其所有出边对应的节点的入度减1，如果某个节点的入度减为0，则将其加入队列中。
		while (!queue.isEmpty()) {
			String curId = queue.poll();
			List<String> nextIds = map.getOrDefault(curId, new ArrayList<>());
			for (String nextId : nextIds) {
				hashMap.put(nextId, hashMap.getOrDefault(nextId, 0) - 1);
				if (hashMap.getOrDefault(nextId, 0) == 0) {
					queue.offer(nextId);
				}
			}
		}

		// 3.最后，判断是否存在入度不为0的节点，如果存在则说明存在环，否则不存在
		for (String key : hashMap.keySet()) {
			if (hashMap.getOrDefault(key, 0) != 0){
				// 存在环
				return false;
			}
		}
		// 不存在环
		return true;
	}


	/**
	 * 删除节点连线
	 *
	 * @param confGraph
	 * @return 通用返回对象
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public R<String> removeLink(ConfGraph confGraph) {
		LambdaUpdateWrapper<ConfGraph> updateWrapper = Wrappers.lambdaUpdate();
		updateWrapper.eq(ConfGraph::getType, GraphType.NET_TOPOLOGY_RELATIONSHIP.getType());
		updateWrapper.eq(ConfGraph::getGraphId, String.valueOf(confGraph.getSystemId()));
		updateWrapper.eq(ConfGraph::getNodeId, String.valueOf(confGraph.getNodeId()));
		updateWrapper.eq(ConfGraph::getParentNodeId, String.valueOf(confGraph.getParentNodeId()));
		return R.ok(confGraphService.remove(updateWrapper) ? "删除节点成功": "删除节点失败");
	}

	/**
	 * 删除节点
	 *
	 * @param confNetTopology
	 * @return 通用返回对象
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public R<String> removeNode(ConfNetTopology confNetTopology) {
		Set<String> childNodeIds = new HashSet<>();
		childNodeIds.add(String.valueOf(confNetTopology.getId()));
		// 递归删除
		if (confNetTopology.getIsRecursiveDelete() != null && confNetTopology.getIsRecursiveDelete()) {
			LambdaQueryWrapper<ConfGraph> lambdaQuery = Wrappers.lambdaQuery();
			lambdaQuery.eq(ConfGraph::getType, GraphType.NET_TOPOLOGY_RELATIONSHIP.getType())
					.eq(ConfGraph::getGraphId, confNetTopology.getSystemId());
			List<ConfGraph> confGraphs = confGraphService.list(lambdaQuery);
			Map<String, List<ConfGraph>> parentAndChildsNodeMap = new HashMap<>();
			for (ConfGraph confGraph : confGraphs) {
				parentAndChildsNodeMap.computeIfAbsent(confGraph.getParentNodeId(), k -> new ArrayList<>()).add(confGraph);
			}
			getChildNodeIds(childNodeIds, parentAndChildsNodeMap, String.valueOf(confNetTopology.getId()));
		}
		// 1. 删除网端
		netTopologyConfigMapper.deleteBatchIds(childNodeIds);

		// 2. 删除组件ip
		LambdaUpdateWrapper<ConfNetTopologyComponentIp> lambdaUpdateWrapper = Wrappers.lambdaUpdate();
		lambdaUpdateWrapper.in(ConfNetTopologyComponentIp::getComponentId, childNodeIds);
		netTopologyComponentMapper.delete(lambdaUpdateWrapper);

		// 3. 删除网端的端到端的关系
		LambdaUpdateWrapper<ConfGraph> lambdaUpdate = Wrappers.lambdaUpdate();
		lambdaUpdate.eq(ConfGraph::getType, GraphType.NET_TOPOLOGY_RELATIONSHIP.getType())
				.eq(ConfGraph::getGraphId, confNetTopology.getSystemId())
				.and(s -> s.eq(ConfGraph::getParentNodeId, confNetTopology.getId())
						.or()
						.in(ConfGraph::getNodeId, childNodeIds));
		confGraphService.remove(lambdaUpdate);

		// 4. 删除节点的信息
		LambdaUpdateWrapper<ConfNodeProperty> updateWrapper = Wrappers.lambdaUpdate();
		updateWrapper.eq(ConfNodeProperty::getType, GraphType.NET_TOPOLOGY_RELATIONSHIP.getType())
				.eq(ConfNodeProperty::getGraphId, confNetTopology.getSystemId())
				.in(ConfNodeProperty::getNodeId, childNodeIds);
		confNodePropertyService.remove(updateWrapper);

		return R.ok("删除节点成功");
	}

	private void getChildNodeIds(Set<String> childNodeIds, Map<String, List<ConfGraph>> parentAndChildsNodeMap, String headNodeId) {
		childNodeIds.add(headNodeId);
		if (parentAndChildsNodeMap.containsKey(headNodeId)){
			List<ConfGraph> confGraphs = parentAndChildsNodeMap.get(headNodeId);
			for (ConfGraph confGraph : confGraphs) {
				getChildNodeIds(childNodeIds, parentAndChildsNodeMap, confGraph.getNodeId());
			}
		}
	}

	/**
	 * 节点移动位置
	 *
	 * @param confNodeProperty
	 * @return 通用返回对象
	 */
	@Override
	public R<String> setLocation(ConfNodeProperty confNodeProperty) {
		LambdaUpdateWrapper<ConfNodeProperty> updateWrapper = Wrappers.lambdaUpdate();
		updateWrapper.eq(ConfNodeProperty::getType, GraphType.NET_TOPOLOGY_RELATIONSHIP.getType());
		updateWrapper.eq(ConfNodeProperty::getGraphId, confNodeProperty.getSystemId());
		updateWrapper.eq(ConfNodeProperty::getNodeId, confNodeProperty.getNodeId());
		updateWrapper.set(ConfNodeProperty::getLocationX, confNodeProperty.getLocationX());
		updateWrapper.set(ConfNodeProperty::getLocationY, confNodeProperty.getLocationY());
		confNodePropertyService.update(updateWrapper);
		return R.ok("移动节点坐标位置成功");
	}

	/**
	 * 供网端监控详情 远程调用的 it路径图接口
	 *
	 * @param confNodeProperty
	 * @return 通用返回对象
	 */
	@Override
	public List<Map<String, Object>> segmentMonitorDetailItRoadGraph(ConfNodeProperty confNodeProperty) {
		// 1. 获取网端信息
		QueryWrapper<ConfSegment> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("system_id", confNodeProperty.getSystemId());
		Map<Integer, String> confSegmentsMap = confSegmentService.list(queryWrapper).stream().collect(Collectors.toMap(ConfSegment::getId, ConfSegment::getName));

		// 2. 获取节点关系信息
		QueryWrapper<ConfGraph> queryWrapperGraph = new QueryWrapper<>();
		queryWrapperGraph.eq("graph_id", confNodeProperty.getSystemId());
		queryWrapperGraph.eq("type", GraphType.IT_ROAD_RELATIONSHIP.getType());
		List<ConfGraph> confGraphs = confGraphService.list(queryWrapperGraph);

		// 3. 获取节点坐标等信息
		LambdaQueryWrapper<ConfNodeProperty> propertyLambdaQueryWrapper = Wrappers.lambdaQuery();
		propertyLambdaQueryWrapper.eq(ConfNodeProperty::getType, confNodeProperty.getType());
		propertyLambdaQueryWrapper.eq(ConfNodeProperty::getGraphId, confNodeProperty.getGraphId());
		Map<String, ConfNodeProperty> nodePropertyIdAndObjectMap = confNodePropertyService.list(propertyLambdaQueryWrapper).stream().collect(Collectors.toMap(ConfNodeProperty::getNodeId, v -> v, (k1, k2) -> k2));

		// 将上面信息组装成 多节点的树结构, 返回给前端
		return generateRoadMap(confSegmentsMap, confGraphs, nodePropertyIdAndObjectMap);
	}

	/**
	 * 修改节点
	 *
	 * @param itRoadVo
	 * @return 通用返回对象
	 */
	@Override
	public R<String> updateNode(ConfNetTopologyVo itRoadVo) {
		LambdaUpdateWrapper<ConfComponentInfoConfig> updateWrapper = Wrappers.lambdaUpdate();
//		updateWrapper.eq(ConfComponentInfoConfig::getSystemId, itRoadVo.getSystemId());
		updateWrapper.eq(ConfComponentInfoConfig::getId, itRoadVo.getId());
		updateWrapper.set(ConfComponentInfoConfig::getConnectType, itRoadVo.getConnectType());
		updateWrapper.set(ConfComponentInfoConfig::getDeviceType, itRoadVo.getDeviceType());
		updateWrapper.set(ConfComponentInfoConfig::getNetType, itRoadVo.getNetType());
		updateWrapper.set(ConfComponentInfoConfig::getComponentName, itRoadVo.getComponentName());
		confComponentInfoConfigService.update(updateWrapper);
		return R.ok("修改数据成功");
	}

	/**
	 * 生成IT路径图
	 * @param confGraphs        节点之间的关系
	 * @param confSegmentsMap  网端id 和 网端信息之间映射
	 * @param nodePropertyIdAndObjectMap
	 * @return 返回通用对象
	 */
	private List<Map<String, Object>> generateRoadMap(Map<Integer, String> confSegmentsMap, List<ConfGraph> confGraphs, Map<String, ConfNodeProperty> nodePropertyIdAndObjectMap) {
		// 构建 父节点 和 子节点一对多关系
		Map<String, List<ConfGraph>> confGraphMap = new HashMap<>();
		for (ConfGraph confGraph : confGraphs) {
			confGraphMap.computeIfAbsent(confGraph.getParentNodeId(), k -> new ArrayList<>()).add(confGraph);
		}

		Set<String> headNodes = new HashSet<>();

		Map<String, Map<String, Object>> map = new HashMap<>();
		List<String> parentNodeIds = confGraphs.stream().map(ConfGraph::getParentNodeId).collect(Collectors.toList());
		for (ConfGraph node : confGraphs) {
			// 找出根节点
			if (!parentNodeIds.contains(node.getNodeId())) {
				headNodes.add(node.getParentNodeId());
			}
			String prev = node.getParentNodeId();
			String cur = node.getNodeId();
			Map<String, Object> treeNode = null;
			treeNode = map.getOrDefault(cur, getTreeNodeInfo(confSegmentsMap, nodePropertyIdAndObjectMap, cur));
			Map<String, Object> parentNode = map.getOrDefault(prev,getTreeNodeInfo(confSegmentsMap, nodePropertyIdAndObjectMap, prev));
			List<Map<String, Object>> childNodes = (List<Map<String, Object>>) parentNode.computeIfAbsent("childNodes", k -> new ArrayList<>());
			childNodes.add(treeNode);
			map.put(prev, parentNode);
			map.put(cur, treeNode);
		}
		List<Map<String, Object>> treeNodes = new ArrayList<>();
		for (String headNode : headNodes) {
			treeNodes.add(map.get(headNode));
		}
		log.info("[配置系统IT路径图] 返回的结果: {}", treeNodes.toString());
		return treeNodes;
	}

	/**
	 *
	 * @param confSegmentsMap
	 * @param nodePropertyIdAndObjectMap
	 * @param cur
	 * @return
	 */
	private Map<String, Object> getTreeNodeInfo(Map<Integer, String> confSegmentsMap, Map<String, ConfNodeProperty> nodePropertyIdAndObjectMap, String cur) {
		Map<String, Object> treeNode = new HashMap<>();
		treeNode.put("id", Integer.valueOf(cur));
		treeNode.put("segmentId", Integer.valueOf(cur));
		treeNode.put("SegmentName", confSegmentsMap.get(Integer.valueOf(cur)));
		if (nodePropertyIdAndObjectMap.get(cur) != null) {
			treeNode.put("locationX",nodePropertyIdAndObjectMap.get(cur).getLocationX());
			treeNode.put("locationY", nodePropertyIdAndObjectMap.get(cur).getLocationY());
		}
		return treeNode;
	}


	/**
	 * 生成IT路径图
	 * @param confComponentIps  ip和port 信息
	 * @param systemId  资源url
	 * @param confGraphs        节点之间的关系
	 * @param confSegmentsMap  网端id 和 网端信息之间映射
	 * @param nodePropertyIdAndObjectMap
	 * @return 返回通用对象
	 */
	private List<Map<String, Object>> generateRoadMap(List<ConfNetTopologyComponentIp> confComponentIps,
													  List<ConfGraph> confGraphs,
													  Map<Integer, String> confSegmentsMap,
													  Map<String, ConfNodeProperty> nodePropertyIdAndObjectMap, Integer systemId) {
		// 构建 组件与 网端之间 一对多关系
		Map<Integer, List<ConfNetTopologyComponentIp>> confComponentIpMap = new HashMap<>();
		for (ConfNetTopologyComponentIp confComponentIp : confComponentIps) {
			confComponentIpMap.computeIfAbsent(confComponentIp.getComponentId(), k -> new ArrayList<>()).add(confComponentIp);
		}

		// 构建 父节点 和 子节点一对多关系
		Map<String, List<ConfGraph>> confGraphMap = new HashMap<>();
		for (ConfGraph confGraph : confGraphs) {
			confGraphMap.computeIfAbsent(confGraph.getParentNodeId(), k -> new ArrayList<>()).add(confGraph);
		}

		Set<String> childNodeSet = new HashSet<>();
		Map<String, Map<String, Object>> map = new HashMap<>();
		for (ConfGraph node : confGraphs) {
			// 找出根节点
			childNodeSet.add(node.getNodeId());
			String prev = node.getParentNodeId();
			String cur = node.getNodeId();
			Map<String, Object> treeNode = null;
			treeNode = map.getOrDefault(cur, getTreeNodeInfo(confSegmentsMap, nodePropertyIdAndObjectMap, confComponentIpMap, cur, systemId));
			Map<String, Object> parentNode = map.getOrDefault(prev,getTreeNodeInfo(confSegmentsMap, nodePropertyIdAndObjectMap, confComponentIpMap, prev, systemId));
			List<Map<String, Object>> childNodes = (List<Map<String, Object>>) parentNode.computeIfAbsent("childNodes", k -> new ArrayList<>());
			childNodes.add(treeNode);
			map.put(prev, parentNode);
			map.put(cur, treeNode);
		}
		List<Map<String, Object>> treeNodes = new ArrayList<>();
		for (String nodeId : nodePropertyIdAndObjectMap.keySet()) {
			if(!childNodeSet.contains(nodeId)){
				if (map.containsKey(nodeId)){
					treeNodes.add(map.get(nodeId));
				}else {
					treeNodes.add(getTreeNodeInfo(confSegmentsMap, nodePropertyIdAndObjectMap, confComponentIpMap, nodeId, systemId));
				}
			}
		}
		return treeNodes;
	}

	/**
	 * 填充节点的各种属性
	 *
	 * @param confSegmentsMap
	 * @param nodePropertyIdAndObjectMap
	 * @param confComponentIpMap
	 * @param systemId
	 * @param cur
	 * @return
	 */
	private Map<String, Object> getTreeNodeInfo(Map<Integer, String> confSegmentsMap,
												Map<String, ConfNodeProperty> nodePropertyIdAndObjectMap,
												Map<Integer, List<ConfNetTopologyComponentIp>> confComponentIpMap,
												String cur, Integer systemId) {

		Map<String, Object> treeNode = new HashMap<>();
		if (confComponentIpMap.containsKey(Integer.valueOf(cur))) {
			treeNode.put("ipAndPort", confComponentIpMap.get(Integer.valueOf(cur)).stream().map(ConfNetTopologyComponentIp::getIp).collect(Collectors.toList()));
		}
		treeNode.put("id", Integer.valueOf(cur));
		ConfComponentInfoConfig configServiceById = confComponentInfoConfigService.getById(cur);
		if (configServiceById != null){
			treeNode.put("name", configServiceById.getComponentName());
		}
		if (nodePropertyIdAndObjectMap.get(cur) != null) {
			treeNode.put("locationX",nodePropertyIdAndObjectMap.get(cur).getLocationX());
			treeNode.put("locationY", nodePropertyIdAndObjectMap.get(cur).getLocationY());
		}

		return treeNode;
	}

}
